package org.finesys.system.service.impl;

import static org.finesys.common.constants.BaseConstant.STATUS_VALID;
import static org.finesys.common.constants.BaseConstant.USER_PASSWORD_VALID_DATE;

import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import org.finesys.common.constants.BaseConstant;
import org.finesys.common.constants.CacheConstants;
import org.finesys.common.core.exception.BusinessException;
import org.finesys.common.core.exception.ErrorCodes;
import org.finesys.common.core.module.R;
import org.finesys.common.core.util.DateUtils;
import org.finesys.common.core.util.MessageUtil;
import org.finesys.common.excel.exception.ErrorMessage;
import org.finesys.common.mp.core.service.impl.BaseServiceImpl;
import org.finesys.common.security.core.util.SecurityUtils;
import org.finesys.common.tenant.support.TenantContextHolder;
import org.finesys.system.api.dto.SysUserDTO;
import org.finesys.system.api.dto.UserInfo;
import org.finesys.system.api.entity.SysDept;
import org.finesys.system.api.entity.SysMenu;
import org.finesys.system.api.entity.SysPost;
import org.finesys.system.api.entity.SysRole;
import org.finesys.system.api.entity.SysUser;
import org.finesys.system.api.entity.SysUserPost;
import org.finesys.system.api.entity.SysUserRole;
import org.finesys.system.api.tenant.SysTenant;
import org.finesys.system.api.vo.UserExcelVO;
import org.finesys.system.api.vo.UserVO;
import org.finesys.system.convert.SysUserConvert;
import org.finesys.system.mapper.SysUserMapper;
import org.finesys.system.mapper.SysUserRoleMapper;
import org.finesys.system.service.SysDeptService;
import org.finesys.system.service.SysMenuService;
import org.finesys.system.service.SysPostService;
import org.finesys.system.service.SysRoleService;
import org.finesys.system.service.SysUserPostService;
import org.finesys.system.service.SysUserService;
import org.finesys.system.service.tenant.SysTenantService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Caching;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@RequiredArgsConstructor
@Slf4j
public class SysUserServiceImpl extends BaseServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    private final SysUserPostService sysUserPostService;

    private final SysRoleService sysRoleService;

    private final SysMenuService sysMenuService;

    private final SysDeptService sysDeptService;

    private final SysPostService sysPostService;

    private final SysTenantService sysTenantService;

    private final SysUserRoleMapper sysUserRoleMapper;

    private final PasswordEncoder passwordEncoder;

    /**
     * 获取用户的全部信息
     */
    @Override
    @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#sysUser.userId")
    public UserInfo getUserInfo(SysUser sysUser) {
        UserInfo userInfo = new UserInfo();
        userInfo.setSysUser(sysUser);
        //設置角色信息
        List<SysRole> roleList = sysRoleService.findRolesByUserId(sysUser.getUserId());
        //角色信息赋值
        userInfo.setRoleList(roleList);
        List<Long> roles = roleList.stream().map(SysRole::getRoleId).collect(Collectors.toList());
        userInfo.setRoles(ArrayUtil.toArray(roles, Long.class));
        //岗位信息
        List<SysPost> postList = sysUserPostService.findPostsByUserId(sysUser.getUserId());
        userInfo.setPostList(postList);
        List<Long> posts = postList.stream().map(SysPost::getPostId).collect(Collectors.toList());
        userInfo.setPosts(ArrayUtil.toArray(posts, Long.class));
        // 设置权限列表（menu.permission）
        Set<String> permissions = new HashSet<>();
        roles.forEach(roleId -> {
            List<String> permissionList = sysMenuService.findMenuByRoleId(roleId)
                    .stream()
                    .filter(menu -> StrUtil.isNotEmpty(menu.getPermission()))
                    .map(SysMenu::getPermission)
                    .collect(Collectors.toList());
            permissions.addAll(permissionList);
        });
        userInfo.setPermissions(ArrayUtil.toArray(permissions, String.class));
        return userInfo;
    }

    /**
     * 根据用户名获取用户
     */
    @Override
    public SysUser findByName(String userName) {
        SysUser sysUser = baseMapper.selectOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUsername, userName));
        if (sysUser == null) {
            throw new BusinessException(MessageUtil.getMessage(ErrorCodes.SYS_USER_USERINFO_EMPTY, userName));
        }
        return sysUser;
    }

    /**
     * 通过用户手机号查找用户信息
     */
    @Override
    public SysUser findByMobile(String mobile) {
        SysUser sysUser = baseMapper.selectOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getPhone, mobile));
        if (sysUser == null) {
            throw new BusinessException(MessageUtil.getMessage(ErrorCodes.SYS_USER_USERINFO_EMPTY, mobile));
        }
        return sysUser;
    }

    /**
     * 通过用户员工号查找用户信息
     */
    @Override
    public SysUser findByUserSN(String userSN) {
        SysUser sysUser = baseMapper.selectOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUserSn, userSN));
        if (sysUser == null) {
            throw new BusinessException(MessageUtil.getMessage(ErrorCodes.SYS_USER_USERINFO_EMPTY, userSN));
        }
        return sysUser;
    }

    /**
     * 删除用户
     */
    @Override
    public Boolean removeUserById(SysUser sysUser) {
        //删除用户角色信息
        sysUserRoleMapper.delete(Wrappers.<SysUserRole>update().lambda().eq(SysUserRole::getUserId, sysUser.getUserId()));
        //删除岗位信息
        sysUserPostService.deleteUserPostByUserId(sysUser.getUserId());
        //删除用户
        return this.removeById(sysUser.getUserId());
    }

    /**
     * 批量删除用户
     */
    @Override
    public Boolean removeUsers(Long[] userIds) {
        //删除用户角色信息
        sysUserRoleMapper.delete(Wrappers.<SysUserRole>update().lambda().in(SysUserRole::getUserId, Arrays.asList(userIds)));
        //删除岗位信息
        sysUserPostService.deleteUserPostByUserIds(userIds);
        //删除用户
        return this.removeByIds(Arrays.asList(userIds));
    }

    /**
     * 保存用户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveUser(SysUserDTO sysUserDto) {
        SysUser sysUser = SysUserConvert.INSTANCE.convert(sysUserDto);
        sysUser.setDelFlag(BaseConstant.NORMAL);
        String password = sysUser.getPassword();
        if (StringUtils.hasText(password)) {
            password = passwordEncoder.encode(password);
        } else {
            password = passwordEncoder.encode(SysUser.DEFAULT_PASSWORD);
        }
        sysUser.setPassword(password);
        //状态
        if (!StringUtils.hasText(sysUser.getStatus())) {
            sysUser.setStatus(STATUS_VALID);
        }
        //密码过期日期，默认为注册日期+90天
        if (Objects.isNull(sysUser.getPasswordExpireDate())) {
            sysUser.setPasswordExpireDate(DateUtils.offsetDay(new Date(), USER_PASSWORD_VALID_DATE));
        }
        //保存用户信息
        this.save(sysUser);
        //保存角色
        Optional.ofNullable(sysUserDto.getRole()).ifPresent(roles -> {
            roles.stream().map(roleId -> {
                SysUserRole sysUserRole = new SysUserRole();
                sysUserRole.setRoleId(roleId);
                sysUserRole.setUserId(sysUser.getUserId());
                return sysUserRole;
            }).forEach(sysUserRoleMapper::insert);
        });
        //保存岗位
        Optional.ofNullable(sysUserDto.getPost()).ifPresent(posts -> {
            posts.stream().map(postId -> {
                SysUserPost sysUserPost = new SysUserPost();
                sysUserPost.setPostId(postId);
                sysUserPost.setUserId(sysUser.getUserId());
                return sysUserPost;
            }).forEach(sysUserPost -> {
                sysUserPostService.save(sysUserPost);
            });
        });

        return Boolean.TRUE;
    }

    /**
     * 更新用户
     */
    @Override
    @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#sysUserDto.userId")
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateUser(SysUserDTO sysUserDto) {
        //更新用户信息
        updateUserInfo(sysUserDto);
        //删除角色
        sysUserRoleMapper.delete(Wrappers.<SysUserRole>update().lambda().eq(SysUserRole::getUserId, sysUserDto.getUserId()));
        //保存角色
        Optional.ofNullable(sysUserDto.getRole()).ifPresent(roles -> {
            roles.stream().map(roleId -> {
                SysUserRole sysUserRole = new SysUserRole();
                sysUserRole.setRoleId(roleId);
                sysUserRole.setUserId(sysUserDto.getUserId());
                return sysUserRole;
            }).forEach(sysUserRoleMapper::insert);
        });
        //删除岗位
        sysUserPostService.deleteUserPostByUserId(sysUserDto.getUserId());
        //保存岗位
        Optional.ofNullable(sysUserDto.getPost()).ifPresent(posts -> {
            posts.stream().map(postId -> {
                SysUserPost sysUserPost = new SysUserPost();
                sysUserPost.setPostId(postId);
                sysUserPost.setUserId(sysUserDto.getUserId());
                return sysUserPost;
            }).forEach(sysUserPost -> {
                sysUserPostService.save(sysUserPost);
            });
        });

        return Boolean.TRUE;
    }

    /**
     * 修改个人信息
     */
    @Override
    @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#sysUserDto.userId")
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateUserInfo(SysUserDTO sysUserDto) {
        SysUser sysUser = SysUserConvert.INSTANCE.convert(sysUserDto);
        sysUser.setDelFlag(BaseConstant.NORMAL);
        String password = sysUser.getPassword();
        if (StringUtils.hasText(password)) {
            password = passwordEncoder.encode(password);
        } else {
            password = null;
        }
        sysUser.setPassword(password);
        //保存用户信息
        this.updateById(sysUser);
        return Boolean.TRUE;
    }

    /**
     * 分页查询用户信息
     */
    @Override
    public IPage<UserVO> getUserVOPage(Page<SysUserDTO> page, SysUserDTO userDTO) {
        return baseMapper.getUserVOPage(page, userDTO);
    }

    /**
     * 通过ID查询用户信息
     *
     * @param id 用户ID
     * @return 用户信息
     */
    @Override
    public UserVO selectUserVoById(Long id) {
        return baseMapper.getUserVoById(id);
    }

    @Override
    @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username")
    public R<?> changePassword(SysUserDTO userDto) {
        SysUser sysUser = baseMapper.selectById(SecurityUtils.getUser().getUserId());
        if (Objects.isNull(sysUser)) {
            return R.failed("用户不存在");
        }

        if (StrUtil.isEmpty(userDto.getPassword())) {
            return R.failed("原密码不能为空");
        }

        if (!passwordEncoder.matches(userDto.getPassword(), sysUser.getPassword())) {
            log.info("原密码错误，修改个人信息失败:{}", userDto.getUsername());
            return R.failed(MessageUtil.getMessage(ErrorCodes.SYS_USER_UPDATE_PASSWORDERROR));
        }

        if (StrUtil.isEmpty(userDto.getNewPassword())) {
            return R.failed("新密码不能为空");
        }
        String password = passwordEncoder.encode(userDto.getNewPassword());

        this.update(Wrappers.<SysUser>lambdaUpdate()
                .set(SysUser::getPassword, password)
                .eq(SysUser::getUserId, sysUser.getUserId()));
        return R.success();
    }

    @Override
    public R<?> checkPassword(String password) {
        SysUser sysUser = baseMapper.selectById(SecurityUtils.getUser().getUserId());
        if (!passwordEncoder.matches(password, sysUser.getPassword())) {
            log.info("原密码错误");
            return R.failed("密码输入错误");
        } else {
            return R.success();
        }
    }

    /**
     * 注册用户 赋予用户默认角色
     *
     * @param userDto 用户信息
     * @return success/false
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> registerUser(SysUserDTO userDto) {
        // 判断用户名是否存在
        SysUser sysUser = this.getOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUsername, userDto.getUsername()));
        if (sysUser != null) {
            String message = MessageUtil.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, userDto.getUsername());
            return R.failed(message);
        }
        validRegisterUser(userDto);
        return R.success(saveUser(userDto));
    }

    /**
     * 校验注册用户信息
     */
    private void validRegisterUser(SysUserDTO userDto) {
        /**用户名校验*/
        if (!StringUtils.hasText(userDto.getName())) {
            userDto.setName(userDto.getUsername());
        }
        //租户信息校验
        if (userDto.getTenantId() == null) {
            Long tenantId = TenantContextHolder.getTenantId();
            if (tenantId == null) {
                tenantId = 1L;
            }
            userDto.setTenantId(tenantId);
        }
    }

    @Override
    @Caching(evict = {
            @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.userId"),
            @CacheEvict(value = CacheConstants.USER_DETAILS, key = "#userDto.username"),
    })
    public R<?> changeStatus(SysUserDTO userDto) {
        SysUser sysUser = baseMapper.selectById(SecurityUtils.getUser().getUserId());
        if (Objects.isNull(sysUser)) {
            return R.failed("用户不存在");
        }
        if (StrUtil.isEmpty(userDto.getStatus())) {
            return R.failed("状态不能为空");
        }
        this.update(Wrappers.<SysUser>lambdaUpdate()
                .set(SysUser::getStatus, userDto.getStatus())
                .eq(SysUser::getUserId, sysUser.getUserId()));
        return R.success();
    }

    @Override
    public R<?> importUser(List<UserExcelVO> userExcelVOList, BindingResult bindingResult) {
        // 通用校验获取失败的数据
        List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
        List<SysDept> deptList = sysDeptService.list();
        List<SysRole> roleList = sysRoleService.list();
        List<SysUser> userList = baseMapper.selectList(Wrappers.emptyWrapper());
        List<SysPost> postList = sysPostService.list();
        List<SysTenant> tenantList = sysTenantService.list();
        for (UserExcelVO excel : userExcelVOList) {

            Set<String> errorMsg = new HashSet<>();
            // 校验用户名是否存在
            boolean exsitUserName = userList.stream()
                    .anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername()));

            if (exsitUserName) {
                errorMsg.add(MessageUtil.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, excel.getUsername()));
            }
            // 判断输入的租户名称列表是否合法
            Optional<SysTenant> tenantOptional = tenantList.stream()
                    .filter(tenant -> excel.getTenantName().equals(tenant.getName()))
                    .findFirst();
            if (!tenantOptional.isPresent()) {
                errorMsg.add(MessageUtil.getMessage(ErrorCodes.SYS_TENANT_TENANTNAME_INEXISTENCE, excel.getTenantName()));
            }

            // 判断输入的部门名称列表是否合法
            Optional<SysDept> deptOptional = deptList.stream()
                    .filter(dept -> excel.getDeptName().equals(dept.getDeptName()))
                    .findFirst();
            if (!deptOptional.isPresent()) {
                errorMsg.add(MessageUtil.getMessage(ErrorCodes.SYS_DEPT_DEPTNAME_INEXISTENCE, excel.getDeptName()));
            }

            // 判断输入的角色名称列表是否合法
            List<String> roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA);
            List<SysRole> roleCollList = roleList.stream()
                    .filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name)))
                    .collect(Collectors.toList());

            if (roleCollList.size() != roleNameList.size()) {
                errorMsg.add(MessageUtil.getMessage(ErrorCodes.SYS_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList()));
            }

            // 判断输入的部门名称列表是否合法
            List<String> postNameList = StrUtil.split(excel.getPostNameList(), StrUtil.COMMA);
            List<SysPost> postCollList = postList.stream()
                    .filter(post -> postNameList.stream().anyMatch(name -> post.getPostName().equals(name)))
                    .collect(Collectors.toList());

            if (postCollList.size() != postNameList.size()) {
                errorMsg.add(MessageUtil.getMessage(ErrorCodes.SYS_POST_POSTNAME_INEXISTENCE, excel.getPostNameList()));
            }

            // 数据合法情况
            if (CollUtil.isEmpty(errorMsg)) {
                insertExcelUser(excel, tenantOptional, deptOptional, roleCollList, postCollList);
            } else {
                // 数据不合法情况
                errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
            }
        }

        if (CollUtil.isNotEmpty(errorMessageList)) {
            return R.failed(errorMessageList);
        }
        return R.success();
    }

    /**
     * 插入Excel数据
     */
    private void insertExcelUser(UserExcelVO excel, Optional<SysTenant> tenantOptional, Optional<SysDept> deptOptional, List<SysRole> roleCollList, List<SysPost> postCollList) {
        SysUserDTO sysUserDTO = SysUserConvert.INSTANCE.convert(excel);
        Long tenantId = tenantOptional.get().getTenantId();
        sysUserDTO.setTenantId(tenantId == null ? 1L : tenantId);
        sysUserDTO.setDeptId(deptOptional.get().getDeptId());
        sysUserDTO.setPost(postCollList.stream().map(SysPost::getPostId).collect(Collectors.toList()));
        sysUserDTO.setRole(roleCollList.stream().map(SysRole::getRoleId).collect(Collectors.toList()));
        this.saveUser(sysUserDTO);
    }


    @Override
    public List<UserExcelVO> listUser(SysUserDTO sysUserDTO) {
        List<UserVO> userVOS = baseMapper.getUserVOList(sysUserDTO);
        return userVOS.stream().map(userVO -> {
            UserExcelVO userExcelVO = SysUserConvert.INSTANCE.convertExcelVO(userVO);
            userExcelVO.setPostNameList(userVO.getPostList().stream().map(SysPost::getPostName).collect(Collectors.joining(",")));
            userExcelVO.setRoleNameList(userVO.getRoleList().stream().map(SysRole::getRoleName).collect(Collectors.joining(",")));
            return userExcelVO;
        }).collect(Collectors.toList());
    }
}
